package com.zx.mes.hyl.security.core.validate.code.impl;

import com.zx.mes.hyl.security.core.validate.code.*;
import com.zx.mes.hyl.security.core.validate.code.exception.ValidateCodeException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;

import java.util.Map;

/**
 * 模板方法类
 * 抽像验证码处理器
 * @author huayunlong
 * @date 2018-6-2
 */
public abstract class AbstractValidateCodeProcessor<C extends ValidateCode> implements ValidateCodeProcessor {

    private Logger logger= LoggerFactory.getLogger(getClass());
    /**
     * 收集系统中所有的 {@Link ValidateCodeGenerator} 接口实现
     */
    @Autowired
    private Map<String,ValidateCodeGenerator> validateCodeGenerators;

    @Autowired
    private ValidateCodeRepository validateCodeRepository;

    @Override
    public void create(ServletWebRequest request) throws Exception {
        /* 第一:生成验证码 **/
        C validateCode=generate(request);
        /* 第二:保存验证码 **/
        save(request,validateCode);
        /* 发送验证码 **/
        send(request,validateCode);
    }



    @Override
    public void validate(ServletWebRequest request) throws ValidateCodeException {
        ValidateCodeType codeType = getValidateCodeType(request);

        C codeInSession = (C) validateCodeRepository.get(request, codeType);

        String codeInRequest;
        try {
            codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),
                    codeType.getParameterNameOnValidate());
        } catch (ServletRequestBindingException e) {
            throw new ValidateCodeException("获取验证码的值失败");
        }

        if (StringUtils.isBlank(codeInRequest)) {
            throw new ValidateCodeException(codeType + "验证码的值不能为空");
        }

        if (codeInSession == null) {
            throw new ValidateCodeException(codeType + "验证码不存在");
        }

        if (codeInSession.isExpired()) {
            validateCodeRepository.remove(request, codeType);
            throw new ValidateCodeException(codeType + "验证码已过期");
        }

        if (!StringUtils.equals(codeInSession.getCode(), codeInRequest)) {
            throw new ValidateCodeException(codeType + "验证码不匹配");
        }

        validateCodeRepository.remove(request, codeType);

    }

    /**
     * 生成校验码
     * @param request
     * @return
     */
    private C generate(ServletWebRequest request) {
        String type=getValidateCodeType(request).toString().toLowerCase();
        String generatorName=type+ValidateCodeGenerator.class.getSimpleName();
        logger.info("验证码名称:[{}]",generatorName);
        ValidateCodeGenerator validateCodeGenerator=validateCodeGenerators.get(generatorName);
        if (validateCodeGenerator ==null){
            throw new ValidateCodeException("验证码生成器:"+ generatorName +"不存在");
        }
        return (C) validateCodeGenerator.generate(request);
    }

    private ValidateCodeType getValidateCodeType(ServletWebRequest request){
        String type= StringUtils.substringBefore(getClass().getSimpleName(),"ValidateCodeProcessor");
        logger.info("请求验证码的类型:[{}]",type);
        return ValidateCodeType.valueOf(type.toUpperCase());
    }

    /**
     * 保存校验码
     * @param request ServletWebRequest
     * @param validateCode C 继承了ValidateCode或本身
     * @see ValidateCode
     */
    private void save(ServletWebRequest request,C validateCode){
        ValidateCode code=new ValidateCode(validateCode.getCode(),validateCode.getExpireTime());
        validateCodeRepository.save(request,code,getValidateCodeType(request));
    }

    /**
     * 发送校验码，由子类实现
     * @param request ServletWebRequest
     * @param validateCode C
     * @throws Exception 抛出异常
     */
    protected abstract void send(ServletWebRequest request,C validateCode) throws Exception;
}
